package com.ywj.websocket.server;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Created with IntelliJ IDEA.
 *
 * @Auther: ywj
 * @Date: 2023/07/28
 * @Description:
 */
@Component
@Slf4j
@ServerEndpoint("/ws/{sid}") //暴露的ws应用的路径
public class WebSocketServer1 {

    /** 当前在线客户端数量（线程安全的）,当前在线连接数 */
    private static AtomicInteger onlineClientNumber = new AtomicInteger(0);

    /** 当前在线客户端集合（线程安全的）：以键值对方式存储，根据sid来获取对应的WebSocket */
    private static Map<String , WebSocketServer1> webSocketMap = new ConcurrentHashMap<>();

    //与某个客户端的连接会话，需要通过它来给客户端发送数据
    private Session session;
    //接收用户的sid，指定需要推送的用户
    private String sid;

    /**
     * 客户端与服务端连接成功后，调用此方法
     * @param session
     * @param sid
     */
    @OnOpen
    public void onOpen(Session session,@PathParam("sid") String sid){
        this.session = session;
        this.sid = sid;
        /*
            do something for onOpen
            与当前客户端连接成功时
         */
        onlineClientNumber.incrementAndGet();//在线数+1
        webSocketMap.put(sid, this);//添加当前连接的WebSocketServer
        log.info("时间[{}]，有新窗口开始监听：{}，当前在线人数为：{}",new Date().toInstant(), sid, onlineClientNumber);
        try {
            sendMessage("连接成功");
        } catch (IOException e) {
            log.error("websocket IO异常");
        }
    }

    /**
     * 客户端与服务端连接关闭
     */
    @OnClose
    public void onClose(){
        /*
            do something for onClose
            与当前客户端连接关闭时
         */
        if(!sid.equals("")){
            onlineClientNumber.decrementAndGet();//在线数-1
        }
        webSocketMap.remove(sid);
        log.info("有一连接关闭！当前在线人数为" + onlineClientNumber);
    }

    /**
     * 客户端与服务端连接异常
     * @param error
     * @param session
     * @param username
     */
    @OnError
    public void onError(Throwable error,Session session,@PathParam("username") String username) {
        /*
            do something for onError
            与当前客户端连接异常时
         */
        error.printStackTrace();
        log.error("发生错误");
    }

    /**
     * 客户端向服务端发送消息
     * @param session
     * @param message
     * @throws IOException
     */
    @OnMessage
    public void onMsg(Session session,String message){
        /*
            do something for onMessage
            收到来自当前客户端的消息时
         */
        log.info("时间[{}]，收到来自窗口 {} 的信息: {}",new Date().toLocaleString(), sid, message);
        if(sid != null){ //群发消息
            sendAllMessage(message);
        } else{  //单独推送
            sendOneMessage(message);
        }
    }

    //向所有客户端发送消息（广播）  服务端主动发
    private void sendAllMessage(String message){
        for (String sid : webSocketMap.keySet()) {
            try{
                webSocketMap.get(sid).sendMessage(message);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    //只向当前客户端发送消息
    private void sendOneMessage(String message){
        if(webSocketMap.get(sid) != null){
            for (String userId : webSocketMap.keySet()) {
                if(userId.equals(sid)){
                    continue;
                }
                try{
                    webSocketMap.get(userId).sendMessage(message);
                } catch (IOException e) {
                    log.error("WebSocketServer|sendToUser[error]:e={}", e.getMessage());
                }
            }
        }

    }

    public void sendMessage(String message) throws IOException {
        this.session.getBasicRemote().sendText(message);//发送消息给客户端
    }

    public static void main(String[] args) throws IOException {

        System.out.println("服务端已启动");
        WebSocketServer1 server = new WebSocketServer1();
        server.sendMessage("Hi, I'm server, nice to meet you");
    }

}



